package cn.wgx.modules.base.util;

import cn.wgx.commons.util.SpringContextHolder;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;

/**
 * 用户对资源点赞，收藏，点击数统计，redis bit一次搞定
 * <p>
 * name为唯一资源表达名，例如： article_like_  为文章点赞
 * article_favorite 为文章收藏
 * 各资源名不能重复
 */
public class Counter {

    private String resName;

    //超过100万ID新建一条数据
    private int MAX_STEP = 1000000;

    private static final String s = "2_";

    private static RedisTemplate redisTemplate;

    private static Set<String> resNames = new HashSet<>();

    static {
        redisTemplate = SpringContextHolder.getBean("redisTemplate");
    }

    /**
     * 资源名称不能重复
     *
     * @param resName 唯一资源名
     * @throws Exception
     */
    public Counter(String resName) throws Exception {
        if (resNames.contains(resName)) {
            throw new Exception("duplicate resName.");
        } else {
            this.resName = resName;
        }
    }

    public Long getCount(int resId) {
        return (Long) redisTemplate.execute((RedisCallback<Long>) connection -> {
            Long result = 0l;
            result = connection.bitCount((resName + resId).getBytes());
            return result;
        }) ;
    }

    public boolean increment(int resId, int userId) {
        return redisTemplate.opsForValue().setBit(resName + s + resId, userId, true);
    }

    public boolean unIncrement(int resId, int userId) {
        return redisTemplate.opsForValue().setBit(resName + s + resId, userId, false);
    }

    public boolean isClick(int resId, int userId) {
        return redisTemplate.opsForValue().getBit(resName + s + resId, userId);
    }

    /**
     * 获取所有点赞的人
     * @param resId
     * @return
     */
    public List getAll(int resId) {
        BitSet bitSet = fromByteArrayReverse((byte[])redisTemplate.execute((RedisCallback<byte[]>) connection -> {
            byte[] result = connection.get((resName + resId).getBytes());
            return result;
        }));
        List<Integer> list = new ArrayList<>();
        for(int n=bitSet.size(),i=1;i<n;i++){
            if(bitSet.get(i)){
                list.add(i);
            }
        }
        return list;
    }

    public boolean clear(int resId) {
        redisTemplate.delete(resName + resId);
        return true;
    }

    private static BitSet fromByteArrayReverse(final byte[] bytes) {
        final BitSet bits = new BitSet();
        if(null == bytes){
            return bits;
        }
        for (int i = 0; i < bytes.length * 8; i++) {
            if ((bytes[i / 8] & (1 << (7 - (i % 8)))) != 0) {
                bits.set(i);
            }
        }
        return bits;
    }
}
